﻿using System;
using System.Collections.Generic;
using Castle.ActiveRecord;
using Atendex.Dominio.Interfaces;
using System.Collections;
using System.Net.Mail;
using System.Text;
using System.Configuration;
using System.IO;

namespace Atendex.Dominio.ActiveRecord
{
    [ActiveRecord("solicitacao")]
    [Serializable]
    public class OrdemServicoAR : ActiveRecordBase<OrdemServicoAR>, IOrdemServico
    {

        private OrdemServicoAR()
        {
            
        }
        internal OrdemServicoAR(IAtendimento atendimento, ITipoSolicitacao tipo, IMensagem mensagem)
            : this(atendimento, tipo, new List<IMensagem> {mensagem})
        {
        }

        internal OrdemServicoAR(IAtendimento atendimento, ITipoSolicitacao tipo, IList<IMensagem> mensagens)
        {
            Atendimento = atendimento;
            Visivel = true;
            Mensagens = mensagens;
            AcrescentarTipo(tipo);
        }

        [PrimaryKey]
        public int Id { get; set; }

        [Property]
        public DateTime? DataInicio { get; set; }

        [Property]
        public DateTime? DataResolucao { get; set; }

        [Property]
        public StatusSolicitacao Status { get; set; }

        [Property]
        public string Email { get; set; }

        private IList<ITipoSolicitacao> tipos = new List<ITipoSolicitacao>();
        [HasAndBelongsToMany(typeof (TipoSolicitacaoAR), Table="ordem_tipo", ColumnKey="ordem_id", ColumnRef="tipo_id")]
        public IList<ITipoSolicitacao> Tipos
        {
            get
            {
                return new List<ITipoSolicitacao>(tipos).AsReadOnly();
            }
            private set
            {
                tipos = value;
            }
        }

        /// <summary>
        /// Acrescenta um tipo de solicitação à ordem de serviço, verificando a consistência.
        /// Todas as solicitações de uma ordem de serviço devem pertencer à mesma área de 
        /// negócios (ou curso).
        /// </summary>
        /// <param name="tipo"></param>
        public void AcrescentarTipo(ITipoSolicitacao tipo)
        {
            if (tipo.AreaNegocio == null)
                throw new DominioException("Você não pode acrescentar um tipo de solicitação sem atrelá-lo a uma área de negócios.");

            if (tipos.Count == 0)
            {
                tipos.Add(tipo);
                this.Area = tipo.AreaNegocio;
            }
            else
            {
                // verifica a área de negócios
                bool ok = true;
                if (this.Area.Id == tipo.AreaNegocio.Id)
                {
                    // se o id for 0 (i.e. objeto não salvo), vê se é o mesmo objeto
                    if (this.Area.Id == 0)
                    {
                        if (this.Area != tipo.AreaNegocio)
                        {
                            ok = false;
                        }
                    }
                }
                else
                    ok = false;

                if (ok)
                {
                    tipos.Add(tipo);
                }
                else
                {
                    throw new DominioException("Uma área de negócios só pode ter solicitações da mesma área. Esta ordem está associada à área " +
                        Area.Nome + ".");
                }
            }
        }

        [HasMany(typeof (MensagemAR), Cascade = ManyRelationCascadeEnum.All, Table = "mensagem",
            ColumnKey = "solicitacaoid")]
        public IList<IMensagem> Mensagens { get; set; }

        [BelongsTo(Type = typeof (AtendimentoAR))]
        public IAtendimento Atendimento { get; set; }

        [Property]
        public bool Visivel { get; set; }

        [BelongsTo(Type=typeof(AreaAR))]
        public IArea Area { get; private set; }

        public void NotificarResponsavel()
        {
            throw new NotImplementedException();
        }

        public void Responder(IMensagem mensagem)
        {
            throw new NotImplementedException();
        }

        public void EnviarEmail()
        {
            string mailTo = String.Empty;
            if(this.Area is IUnidadeNegocio) 
            {
                IUnidadeNegocio un = (IUnidadeNegocio)this.Area;
                mailTo = un.Responsavel.Email;
            }
            else if(this.Area is ICurso)
            {
                ICurso curso = (ICurso)this.Area;
                foreach(IResponsavel resp in curso.Responsaveis)
                    mailTo = resp.Email + ", ";
            }

            MailMessage message = new MailMessage(ConfigurationManager.AppSettings["EnviadorDeEmail"], mailTo);
            message.Subject = "Solicitação cadastrada na Central de Atendimento";
            string mailBody = File.ReadAllText(Environment.CurrentDirectory + @"\Atendex.Dominio\EmailTemplates\EnvioSolicitacao.htm");
            mailBody = mailBody.Replace("{0}", this.Atendimento.Id.ToString());
            mailBody = mailBody.Replace("{1}", this.GeraIdCifrado());
            mailBody = mailBody.Replace("{2}", this.Mensagens.ToString());

            message.Body = mailBody;
            message.IsBodyHtml = true;

            SmtpClient client = new SmtpClient();
            client.EnableSsl = true;
            client.Send(message);
        }

        /// <summary>
        /// Gera um identificador encriptado da ordem de serviço.
        /// </summary>
        /// <returns></returns>
        public string GeraIdCifrado()
        {
            return Cryptex.EncriptaChavePadrao(Id.ToString());
        }
        /// <summary>
        /// Recupera um id cifrado e retorna o id original, ou null se estiver corrompido
        /// </summary>
        /// <param name="idCifrado"></param>
        /// <returns></returns>
        public int? RecuperaIdCifrado(string idCifrado)
        {
            try
            {
                string textoClaro = Cryptex.DecriptaChavePadrao(idCifrado);
                int id;
                if (int.TryParse(textoClaro, out id))
                    return id;
                else
                    return null;
            }
            catch (CryptexException)
            {
                return null;
            }
        }

    }
}